#!/usr/bin/env python

import random

class MultiNode:
    
    def __init__(self, name):
        assert name != None
        assert name != ""
        
        self.name = name
        self.instances = {}      
        self.free_instances = []
    
    def get_random_instance(self):
        instance = random.choice(self.instance_list())
        assert instance != None
        return [self.instances[instance], instance]
    
    def neighbours(self, instance):
        neighbours = []
        for edge in self.instances[instance]:
            neighbours.append(edge[0])
        return neighbours
    
    def all_neighbours(self):
        neighbours = []
        for instance in self.instance_list():
            neighbours.extend(self.neighbours(instance))
        return neighbours
    
    def add_edge(self, destination, payload, instance):
        """
        adds an outbound edge to <instance> pointed at <destination> with a <payload>
        """
        assert destination != None
        
        if instance in self.instances: # if instance already exists
            edges = self.instances[instance] # get its edges
        else:
            edges = [] # make a new edges list
        
        edge = [destination, payload] #build the edge
        edges.append(edge)   # append it to edges list  
        self.instances[instance] = edges # add the edges list to instance
    
    def remove_edges(self, mnode):
        """
        removes all edges destined to mnode
        """
        for instance, edges in self.instances.iteritems(): #go through instances
            for edge in edges:     #go through edges
                if mnode in edge:   #if there is an edge towards mnode
                    edges.remove(edge)  #delete it
    
    def remove_instance(self, instance):
        """
        removes the given instance
        """
        del self.instances[instance]
        self.free_instances.append(instance) # add the freed instance to free instances list to keep instance naming consistence
    
    def duplicate_instance(self, instance):
        """
        duplicates the given instance
        """
        next_instance = self.next_instance()
        self.instances[next_instance] = self.instances[instance] # new instance name is: lenght of table + 1 -> 1,2,3 etc
        #self.neighbours[] = self.neighbours[instance]
    
    def next_instance(self):
        if len(self.free_instances) != 0: # if there are used free instances
            return self.free_instances.pop() # return one
        else:
            return str(len(self.instance_list()) + 1) # else return the next instance number
    
    def instance_list(self):
        return self.instances.keys()
    
    def __contains__(self,n):
        """
        Allows the expression 'n in G'.
        Testing whether an unhashable object, such as a list, is in the
        dict datastructure (self.adj) will raise a TypeError.
        Rather than propagate this to the calling method, just
        return False.
        """
        try:
            return b
        except TypeError:
            return False
    
    def __getitem__(self,n):
        """Return the neighbors of node n as a list.
        This provides graph G the natural property that G[n] returns
        the neighbors of G. 
        """
        return self.instances[n]
 
def print_mnode(mnode):
    ret = mnode.name + " "
    for val in mnode.instances.keys():
        ret += str(val) + " "
    return ret

def print_instance(instance):
    ret = ""
    for l in instance:
        ret += "   -> " + l[0] + "\n"
    return ret